#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <assert.h>

typedef unsigned int   uint;
typedef unsigned short ushort;
typedef unsigned char  uchar;

typedef unsigned char uint8;
typedef unsigned short uint16;
typedef unsigned int  uint32;
typedef unsigned long uint64;




#define ATTR_READ_ONLY      0x01
#define ATTR_HIDDEN         0x02
#define ATTR_SYSTEM         0x04
#define ATTR_VOLUME_ID      0x08
#define ATTR_DIRECTORY      0x10
#define ATTR_ARCHIVE        0x20
#define ATTR_DEVICE         0x40
#define ATTR_LONG_NAME      0x0F

#define LAST_LONG_ENTRY     0x40
#define FAT32_EOC           0x0ffffff8
#define EMPTY_ENTRY         0xe5
#define END_OF_ENTRY        0x00
#define CHAR_LONG_NAME      13
#define CHAR_SHORT_NAME     11

#define FAT32_MAX_FILENAME  255
#define FAT32_MAX_PATH      260
#define ENTRY_CACHE_NUM     50

#define DEBUG 1
#define BSIZE 512  // block size
#define FSSIZE       3000  // size of file system in blocks
#define CLUSERSIZE 128
#define HIDDSEC 20
#define TOTALSEC 10000
#define FATSEC  625
static struct {
    uint32  first_data_sec;
    uint32  data_sec_cnt;
    uint32  data_clus_cnt;
    uint32  byts_per_clus;

    struct {
        uint16  byts_per_sec;
        uint8   sec_per_clus;
        uint16  rsvd_sec_cnt;
        uint8   fat_cnt;            /* count of FAT regions */
        uint32  hidd_sec;           /* count of hidden sectors */
        uint32  tot_sec;            /* total count of sectors including all regions */
        uint32  fat_sz;             /* count of sectors for a FAT region */
        uint32  root_clus;
    } bpb;

} fat;



#define FSMAGIC 0x10203040




struct dirent {
    char  filename[255 + 1];
    uint8   attribute;
    uint32  first_clus;
    uint32  file_size;

    uint32  cur_clus;
    uint    clus_cnt;

    /* for OS */
    uint8   dev;
    uint8   dirty;
    uint8   valid;
    int     ref;
    uint32  off;            // offset in the parent dir entry, for writing convenience
    struct dirent *parent;  // because FAT32 doesn't have such thing like inum, use this for cache trick
    struct dirent *next;
    struct dirent *prev;
}root;

struct buf {
  int valid;   // has data been read from disk?
  int disk;    // does disk "own" buf?
  uint dev;
  uint blockno;
  uint refcnt;
  struct buf *prev; // LRU cache list
  struct buf *next;
  uchar data[BSIZE];
};

//#include "kernel/stat.h"
#define T_DIR     1   // Directory
#define T_FILE    2   // File
#define T_DEVICE  3   // Device




int fsfd;
char zeroes[BSIZE];
char *firstfile = "Welcome to thinos file system.\n";
void wsect(uint, void*);
void rsect(uint sec, void *buf);
void bwrite(struct buf *b);
struct buf* bread(uint dev, uint blockno);
void brelse(struct buf *b);
static uint rw_clus(uint32 cluster, int write, int user, uint64 data, uint off, uint n);

/**
 * For the given number of a data cluster, return the number of the sector in a FAT table.
 * @param   cluster     number of a data cluster
 * @param   fat_num     number of FAT table from 1, shouldn't be larger than bpb::fat_cnt
 */
static inline uint32 fat_sec_of_clus(uint32 cluster, uint8 fat_num)
{
    return fat.bpb.rsvd_sec_cnt + (cluster << 2) / fat.bpb.byts_per_sec + fat.bpb.fat_sz * (fat_num - 1);
}

/**
 * For the given number of a data cluster, return the offest in the corresponding sector in a FAT table.
 * @param   cluster   number of a data cluster
 */
static inline uint32 fat_offset_of_clus(uint32 cluster)
{
    return (cluster << 2) % fat.bpb.byts_per_sec;
}
static inline uint32 first_sec_of_clus(uint32 cluster)
{
    return ((cluster - 2) * fat.bpb.sec_per_clus) + fat.first_data_sec;
}

/**
 * Generate an entry in the raw type and write to the disk.
 * @param   data        for s-n-e it's the first cluster, for l-n-e it's the ordinal.
 * @param   checksum    only for l-n-e, the checksum.
 */
static void make_entry(struct dirent *dp, uint off, char *name, uint8 attr, uint32 data, uint8 checksum)
{
    uint8 ebuf[32] = {0};
    if ((ebuf[11] = attr) == ATTR_LONG_NAME) {
        ebuf[0] = data;
        ebuf[13] = checksum;
        name += ((data & ~LAST_LONG_ENTRY) - 1) * CHAR_LONG_NAME;
        uint8 *w = ebuf + 1;
        int end = 0;
        for (int i = 1; i <= CHAR_LONG_NAME; i++) {
            if (end) {
                *w++ = 0xff;            // on k210, unaligned reading is illegal
                *w++ = 0xff;
            } else { 
                if ((*w++ = *name++) == 0) {
                    end = 1;
                }
                *w++ = 0;
            }
            switch (i) {
                case 5:     w = ebuf + 14; break;
                case 11:    w = ebuf + 28; break;
            }
        }
    } else {
        
        strncpy((char *)ebuf, name, 11);
        *(uint16 *)(ebuf + 20) = (uint16)(data >> 16);      // first clus high 16 bits
        *(uint16 *)(ebuf + 26) = (uint16)(data & 0xff);     // low 16 bits
        *(uint32 *)(ebuf + 28) = 0;                         // filesize is updated in eupdate()
    }
 //   off = reloc_clus(dp, off, 1);
    rw_clus(dp->cur_clus, 1, 0, (uint64)ebuf, off, sizeof(ebuf));

}

/**
 * Read the FAT table content corresponded to the given cluster number.
 * @param   cluster     the number of cluster which you want to read its content in FAT table
 */
static uint32 read_fat(uint32 cluster)
{
    if (cluster >= FAT32_EOC) {
        return cluster;
    }
    if (cluster > fat.data_clus_cnt + 1) {     // because cluster number starts at 2, not 0
        return 0;
    }
    uint32 next_clus;
    
    uint32 fat_sec = fat_sec_of_clus(cluster, 1);
    // here should be a cache layer for FAT table, but not implemented yet.
    struct buf *b = bread(0, fat_sec);
    next_clus = *(uint32 *)(b->data + fat_offset_of_clus(cluster));
    brelse(b);
    return next_clus;
}
/**
 * Write the FAT region content corresponded to the given cluster number.
 * @param   cluster     the number of cluster to write its content in FAT table
 * @param   content     the content which should be the next cluster number of FAT end of chain flag
 */
static int write_fat(uint32 cluster, uint32 content)
{
    if (cluster > fat.data_clus_cnt + 1) {
        return -1;
    }
    uint32 fat_sec = fat_sec_of_clus(cluster, 1);
    struct buf *b = bread(0, fat_sec);
    uint off = fat_offset_of_clus(cluster);
    *(uint32 *)(b->data + off) = content;
    bwrite(b);
    brelse(b);
    return 0;
}
int
either_copyin(void *dst, int user_src, uint64 src, uint64 len)
{
  
  memmove(dst, (char*)src, len);
  return 0;
}
int
either_copyout(int user_dst, uint64 dst, void *src, uint64 len)
{
  
  memmove((char *)dst, src, len);
  return 0;
  
}
static uint rw_clus(uint32 cluster, int write, int user, uint64 data, uint off, uint n)
{

    uint tot, m;
    struct buf *bp;
    uint sec = first_sec_of_clus(cluster) + off / fat.bpb.byts_per_sec;
    off = off % fat.bpb.byts_per_sec;

    int bad = 0;
    for (tot = 0; tot < n; tot += m, off += m, data += m, sec++) {
        bp = bread(0, sec);
        m = BSIZE - off % BSIZE;
        if (n - tot < m) {
            m = n - tot;
        }
        if (write) {
            if ((bad = either_copyin(bp->data + (off % BSIZE), user, data, m)) != -1) {
                bwrite(bp);
            }
        } else {
            bad = either_copyout(user, data, bp->data + (off % BSIZE), m);
        }
        brelse(bp);
        if (bad == -1) {
            break;
        }
    }
    return tot;
}
int fillfat(uint32 cluster, int content)
{
  struct buf *b;
  uint32 sec = fat.bpb.rsvd_sec_cnt;
  b = bread(0, sec);
  uint32 j = cluster;
  ((uint32 *)(b->data))[j] = content;
  bwrite(b);
  brelse(b);
  return 1;
}
void disk_rw(struct buf *b, int write)
{
  uint64 offset=b->blockno*BSIZE;
  //  printf("file def %d\n",img_file.refcnt);
  if(write){
    //write the disk
    ssize_t ret=pwrite(fsfd,b->data,BSIZE,offset);
  }else
  {
  
    //read  the disk
    ssize_t ret = pread(fsfd, b->data, BSIZE, offset);
    if(ret!=BSIZE){
      printf("ret : %d\n",ret);
    }
  }

}
int
main(int argc, char *argv[])
{
  int i, cc, fd;
  uint rootino, inum, off;
  struct dirent de;
  char buf[BSIZE];
  FILE *fp;

  static_assert(sizeof(int) == 4, "Integers must be 4 bytes!");

  if(argc < 2){
    fprintf(stderr, "Usage: mkfs fs.img files...\n");
    exit(1);
  }

  fprintf(stderr, "open %s\n",argv[1]);
  //打开image fs.img
   fsfd = open(argv[1], O_RDWR|O_CREAT|O_TRUNC, 0666);
  if(fsfd < 0){
    perror(argv[1]);
    exit(1);
  }


  //初始化各个扇区（扇区大小等于block大小）
  for(i = 0; i < FSSIZE; i++)
    wsect(i, zeroes);
    
  //写super　block
  memset(buf, 0, sizeof(buf));
  uint16 byte_per_sec = BSIZE;
  memmove(buf+11,&byte_per_sec,2);
  uint8 sec_per_clus = CLUSERSIZE;
  memmove(buf+13,&sec_per_clus,1);
  uint16 rsvd_sec_cnt = 20;
  memmove(buf+14,&rsvd_sec_cnt,2);
  uint8 fat_cnt = 2;
  memmove(buf+16,&fat_cnt,1);
  uint32 hidd_sec = HIDDSEC;
  memmove(buf+28,&hidd_sec,4);
  uint32 tot_sec = TOTALSEC;
  memmove(buf+32,&tot_sec,4);
  uint32 fat_sz = FATSEC;
  memmove(buf+36,&fat_sz,4);
  uint32 root_clus = 2;
  memmove(buf+44,&root_clus,4);
  char *name = "FAT32";
  memmove(buf+82,name,5);

  wsect(0, buf);
  char b[BSIZE];
  rsect(0, b);
  // fat.bpb.byts_per_sec = *(uint16 *)(b + 11);
  memmove(&fat.bpb.byts_per_sec, b + 11, 2);       //每个sector多少字节     // avoid misaligned load on k210
  fat.bpb.sec_per_clus = *(b + 13);             //每个簇几个sector
  fat.bpb.rsvd_sec_cnt = *(uint16 *)(b + 14);//保留扇区数
  printf("fffk%d\n",fat.bpb.rsvd_sec_cnt);
  fat.bpb.fat_cnt = *(b + 16);              //FAT个数
  fat.bpb.hidd_sec = *(uint32 *)(b + 28);   //隐藏扇区数
  fat.bpb.tot_sec = *(uint32 *)(b + 32);    //总扇区数
  fat.bpb.fat_sz = *(uint32 *)(b + 36);     //FAT表占用扇区数
  fat.bpb.root_clus = *(uint32 *)(b + 44);  //第一个簇的簇号
  fat.first_data_sec = fat.bpb.rsvd_sec_cnt + fat.bpb.fat_cnt * fat.bpb.fat_sz;
  fat.data_sec_cnt = fat.bpb.tot_sec - fat.first_data_sec; //数据区扇区数目
  fat.data_clus_cnt = fat.data_sec_cnt / fat.bpb.sec_per_clus; //数据区簇数目
  fat.byts_per_clus = fat.bpb.sec_per_clus * fat.bpb.byts_per_sec;  //每个簇的字节数
  if(DEBUG)
  {
    printf("[FAT32 init]byts_per_sec: %d\n", fat.bpb.byts_per_sec);
    printf("[FAT32 init]root_clus: %d\n", fat.bpb.root_clus);
    printf("[FAT32 init]sec_per_clus: %d\n", fat.bpb.sec_per_clus);
    printf("[FAT32 init]fat_cnt: %d\n", fat.bpb.fat_cnt);
    printf("[FAT32 init]fat_sz: %d\n", fat.bpb.fat_sz);
   printf("[FAT32 init]tot_sec: %d\n", fat.bpb.tot_sec);  
	printf("[FAT32 init]first_data_sec: %d\n", fat.first_data_sec);  
  }
  
  root.first_clus = fat.bpb.root_clus;
  root.cur_clus = fat.bpb.root_clus;
  make_entry(&root, 0, ".", ATTR_DIRECTORY, root.first_clus, 0);
  make_entry(&root, 32, "..", ATTR_DIRECTORY, root.first_clus, 0);
  make_entry(&root,64,"firstfile",ATTR_ARCHIVE,3,0);
  fillfat(3,FAT32_EOC+7);
    rw_clus(3, 1, 1, firstfile, 0, strlen(firstfile) );
  make_entry(&root,96,"dev",ATTR_DIRECTORY,4,0);
  struct dirent *dp = (struct dirent *)malloc(sizeof(struct dirent));
  dp->first_clus = 4;
  dp->cur_clus = 4;
  make_entry(dp, 0, ".", ATTR_DIRECTORY, dp->first_clus, 0);
  make_entry(dp, 32, "..", ATTR_DIRECTORY, root.cur_clus, 0);
  make_entry(dp, 32, "dsp", ATTR_DEVICE, 5, 0);
  fillfat(4,FAT32_EOC+7);
  fillfat(5,FAT32_EOC+7);

  // 下列是你需要完成的任务
  //读取root的前三个目录项
  rw_clus(2,0,0,buf,0,32);
  printf("pathname : %s\n",buf);
  printf("first cluster : %d\n",(int)*(short *)(buf+26));
  rw_clus(2,0,0,buf,32,32);
  printf("pathname : %s\n",buf);
  printf("first cluster : %d\n",(int)*(short *)(buf+26));
  rw_clus(2,0,0,buf,64,32);
  printf("pathname : %s\n",buf);
  printf("first cluster : %d\n",(int)*(short *)(buf+26));

  //完成read_fat函数,输出第三个目录项对应的第一个簇号在fat表中的内容
  uint32 content = read_fat((uint32)*(short *)(buf+26));
  printf("fat32:0x%x\n",content);

  

  exit(0);
}

void
wsect(uint sec, void *buf)
{
  if(lseek(fsfd, sec * BSIZE, 0) != sec * BSIZE){
    perror("lseek");
    exit(1);
  }
  if(write(fsfd, buf, BSIZE) != BSIZE){
    perror("write");
    exit(1);
  }
}




void
rsect(uint sec, void *buf)
{
  if(lseek(fsfd, sec * BSIZE, 0) != sec * BSIZE){
    perror("lseek");
    exit(1);
  }
  if(read(fsfd, buf, BSIZE) != BSIZE){
    perror("read");
    exit(1);
  }
}


struct buf* bread(uint dev, uint blockno)
{


   struct buf * b = (struct buf *)malloc(sizeof(struct buf));
   b->dev = dev;
   b->blockno = blockno;
   b->valid = 0;
   b->refcnt = 1;
  if(!b->valid) {
    disk_rw(b, 0);
    b->valid = 1;
  }
  return b;
}


// Write b's contents to disk.  Must be locked.
void bwrite(struct buf *b)
{
  disk_rw(b, 1);
}

void brelse(struct buf *b)
{
  free(b);
}
